/*
 * SmartCam master processor library
 * Greedy coprocessor mapping.
 */

#include <float.h>
#include <log.h>
#include <math.h>

#include "globals.h"
#include "mapping.h"
#include "evaluate.h"
#include "greedy.h"

#define SC_MAP_UNMAPPED_PENALIZE       1000
#define SC_MAP_UNMAPPED_CYCLE_PENALIZE 100000

extern table_t scm_procs, scm_ops, scm_streams, /* owned, indexed */
               scm_links;                       /* owned */

double scMapGetPenalty(int pm, int refinements)
{
  switch (refinements)
  {
    case 0:
      return 1;
    case 1:
      switch (pm)
      {
        case 0:
          return 100;
        default:
          return 1;
      }
    default:
      if (pm < refinements)
      {
        unsigned long long int r = pm;
        
        if ((pm%2)==1)
        {
          return 1-1.0/pow(1.2, r);
        }
        else
        {
          return 1+16.0/pow(1.2, r);
        }
      }
      else
        return 1;
  }
}

int scMapGreedy(int accuracy)
{
  int id=-1, id2=-1, dof=0, fail=0;
  int pm;
  table_t mapping; /* scm_proc_t */
  double value, best=-DBL_MAX;
  scm_proc_t *p;
  scm_op_t *o;
  table_t mappingorder;
  int refinements;
  
  if (accuracy)
    refinements = scs_mapper.iterations;
  else
    refinements = 0;

  /* Initialize mapping */
  abortOnNegative(STREAM(scl.err), tblcreate(&mapping, sizeof(scm_proc_t)));
  abortOnNegative(STREAM(scl.err), tblcreate(&mappingorder, sizeof(scm_op_t)));

  while (tblgetnextelement(&scm_ops, &id, &o))
  {
    if (!o->fixed)
    {
      dof++;
      tbladd(&mappingorder, o);
    }
  }

  if (dof)
  {
    lprintf(STREAM(scl.info), "Finding a greedy mapping for %d operations", dof);
  }
  else
  {
    lprintf(STREAM(scl.info), "Unable to map any operations");
    abortOnNegative(STREAM(scl.err), tbldestroy(&mapping, 0));
    abortOnNegative(STREAM(scl.err), tbldestroy(&mappingorder, 0));
    return 1;
  }

  for (pm=0; pm < refinements+1; pm++)
  {
    if (logEdit(scl.crawl, LOG_STREAM_GET_ENABLED))
    {
      char buf[PATH_MAX] = {0};

      while (tblgetnextelement(&mappingorder, &id, &o))
	sprintf(&buf[strlen(buf)], "%d ", o->id);

      lprintf(STREAM(scl.crawl), "Mapping order [ %s]", buf);
    }

    if (!pm || pm == refinements)
    {
      /* Clear mapping */
      id = -1;
      while (tblgetnextelement(&mappingorder, &id, &o))
        if (o->processor) scMapUnassignProcessor(o);
    }

    /* Find greedy mapping */
    id = -1;
    while (tblgetnextelement(&mappingorder, &id, &o))
    {
      double obest=-DBL_MAX;
      scm_proc_t *bestproc=NULL;
      
      //lprintf(STREAM(scl.debug), "Operation %d", o->id);

      /* Try all possible processors for this operation */
      id2 = -1;
      while (tblgetnextelement(&scm_procs, &id2, &p))
      {
	if (tblget(&p->target->operations, o->operation->id, NULL))
	{
	  /* Next victim */  
	  if (o->processor) scMapUnassignProcessor(o);

	  if (!scMapAssignProcessor(o, p))
	  {
	    value = scEvlMapping(scMapGetPenalty(pm, refinements));

            //lprintf(STREAM(scl.debug), "Processor %s: %f", p->processor->name, value);

	    if (value > obest)
	    {
	      bestproc = p;
	      obest = value;

 	      if (!accuracy)
	        break;
	    }
	  }
	}
      }

      /* Keep best mapping for this operation */
      if (bestproc)
      {
	if (o->processor != bestproc)
	{
	  if (o->processor) scMapUnassignProcessor(o);
	  abortOnError(STREAM(scl.err), scMapAssignProcessor(o, bestproc));
	}
      }
      else;
	/* Operation couldn't be mapped */
    }

    value = scEvlMapping(1);
    printf("%% Refinement %d (penalty %f): %f\n", pm,
           scMapGetPenalty(pm, refinements), 1.0/value);
/*
    lprintf(STREAM(scl.crawl), "Refinement %d (penalty %f): %f", pm,
            scMapGetPenalty(pm, refinements), 1.0/value);
*/
        
    /* Penalize for unmapped operations */
    id=-1;
    while (tblgetnextelement(&scm_ops, &id, &o))
      if (!o->fixed && !o->processor)
      {
	if (o->operation->id == CYCLE)
	  value = 1/(1/value + SC_MAP_UNMAPPED_CYCLE_PENALIZE);
	else
	  value = 1/(1/value + SC_MAP_UNMAPPED_PENALIZE);
      }

    /* Remember best mapping */
    if (value > best)
    {
      abortOnNegative(STREAM(scl.err), tblclear(&mapping, 0));
      id = -1;
      while (tblgetnextelement(&scm_ops, &id, &o))
	if (o->processor)
	  abortOnNegative(STREAM(scl.err), tbladdi(&mapping, o->processor, o->id));
      best = value;
    }
  }

  /* Clear mapping */
  id = -1;
  while (tblgetnextelement(&scm_ops, &id, &o))
    if (!o->fixed && o->processor)
      scMapUnassignProcessor(o);

  /* Set to best mapping */
  id = -1;
  while (tblget(&scm_ops, id = tblgetnexti(&scm_ops, id), &o))
  {
    tblget(&mapping, o->id, &p);
    if (!o->fixed && p)
      abortOnError(STREAM(scl.err), scMapAssignProcessor(o, p));

    if (!o->processor)
      fail = 1;
  }

  abortOnNegative(STREAM(scl.err), tbldestroy(&mapping, 0));
  abortOnNegative(STREAM(scl.err), tbldestroy(&mappingorder, 0));

  return fail;
}
